<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>模拟 vue 响应式原理</title>
  </head>
  <body>
    <script>
      // 数据类型检测
      function detectionDataType(targetObj) {
        return Object.prototype.toString.call(targetObj).slice(8, -1);
      }

      // 数据代理
      function dataBrokers(targetObj, proxyObj) {
        for (let key of Object.keys(targetObj)) {
          Object.defineProperty(proxyObj, key, {
            get() {
              return targetObj[key];
            },
            set(newVal) {
              targetObj[key] = newVal;
            },
          });
        }
      }

      // 数据劫持
      function deep(targetObj, attribute, val) {
        // 对 val 进行深度劫持
        dataHijacked(val);
        Object.defineProperty(targetObj, attribute, {
          get() {
            return val;
          },
          set(newVal) {
            val = newVal;
          },
        });
      }
      function dataHijacked(targetObj) {
        // 判断 val 的值是否可以进行深度劫持
        if (detectionDataType(targetObj) !== "Object" && detectionDataType(targetObj) !== "Array") return;
        for (let [key, value] of Object.entries(targetObj)) {
          deep(targetObj, key, value);
        }
      }

      // 1.创建 vue 构造函数
      function Vue(options) {
        // 2.将配置对象中的 data 赋值给 vm 上的 _data 属性
        switch (detectionDataType(options.data)) {
          case "Object":
            this._data = options.data;
            break;
          case "Function":
            this._data = options.data();
            break;
          default:
            throw new Error("Vue 配置对象中的 data 应该为一个对象或方法！！！");
        }
        // 3.代理 _data 中的数据到 vm 上（只代理第一层）
        dataBrokers(this._data, this);
        // 4.劫持 _data 中的所有数据（自己代理自己）
        dataHijacked(this._data);
      }

      // 实例化 Vue
      const vm = new Vue({
        data() {
          return {
            name: "张三",
            age: 20,
            friends: [
              {name: "土豪1", age: "3"},
              {name: "土豪2", age: "6"},
            ]
          };
        },
      });
    </script>
  </body>
</html>
